// Package job
// Created by GoLand
// @User: lenora
// @Date: 2024/8/16
// @Time: 15:52

package job

import (
	"camp/microbook/internal/service"
	"camp/microbook/pkg/logger"
	"context"
	"golang.org/x/sync/semaphore"
	"time"
)

type Schedule struct {
	dbTimeout time.Duration
	svc       service.JobService
	limiter   *semaphore.Weighted
	executors map[string]Executor
	l         logger.Logger
}

func NewSchedule(svc service.JobService, l logger.Logger) *Schedule {
	return &Schedule{
		dbTimeout: time.Second,
		svc:       svc,
		limiter:   semaphore.NewWeighted(100),
		executors: make(map[string]Executor),
		l:         l,
	}
}

// Run  调度定时任务
// 持续抢占定时任务-找到其执行器-更新下次执行时间
//
// @return error
func (s *Schedule) Run(ctx context.Context) error {
	for {
		if ctx.Err() != nil {
			//ctx出错,不再调度
			return ctx.Err()
		}
		if err := s.limiter.Acquire(ctx, 1); err != nil {
			return err
		}
		dbCtx, cancel := context.WithTimeout(ctx, s.dbTimeout)
		job, err := s.svc.Preempt(dbCtx)
		cancel()
		if err != nil {
			s.limiter.Release(1)
			continue
		}
		exe, ok := s.executors[job.Executor]
		if !ok {
			s.l.Error("executors not exist", logger.String("name", job.Executor))
			s.limiter.Release(1)
			continue
		}

		go func() {
			//根据job的executor执行其对应的任务,并释放掉
			defer func() {
				s.limiter.Release(1)
				job.CancelFunc()
			}()
			//执行job任务
			err = exe.Execute(ctx)
			if err != nil {
				s.l.Error("execute job error",
					logger.String("name", exe.Name()),
					logger.Error(err),
				)
				return
			}
			//重置下次执行时间
			err = s.svc.ResetNextTime(ctx, job)
			if err != nil {
				s.l.Error("reset next time of job executing failed",
					logger.Uint64("id", job.Id),
					logger.String("name", exe.Name()),
					logger.Error(err),
				)
			}
		}()
	}
}

// Register  注册执行器
//
// @param executor
func (s *Schedule) Register(executor Executor) {
	s.executors[executor.Name()] = executor
}
